﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Linq;
using AutoMapper;
using System.Globalization;
using System.Collections.Specialized;
using System.Threading;
using System.Data;
using System.Runtime.CompilerServices;

namespace NMA.Application
{
    using NMA.Domain.Model.Repository;
    using NMA.Infrastructure.EventAggregator;
    using NMA.Infrastructure.NHibernate.Service.Event;
    using NMA.Infrastructure.Logging;
    using NMA.Infrastructure.NHibernate.Container;
    using NMA.Infrastructure.DBC;
    using NMA.Infrastructure.NHibernate.DomainObject;
    using NMA.Domain.Model;
    using NMA.Domain.Shared;
    using NMA.Infrastructure.LambdaExpression;
    using NMA.Domain.Model.Service.Application;
    using NMA.Domain.Model.DTO;
    using NMA.Domain.Shared.Paging;
    using NMA.Infrastructure.NHibernate.Paging;

    public class ResourceFacade : IResourceFacade
    {
        #region variables

        private readonly IResourceRepository _resourceRepository = null;
        private readonly IConsumer<AuditEvent> _handler = null;
        private readonly IAuditFactory _auditFactory = null;
        private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        #endregion

        #region ctors

        public ResourceFacade()
            : this(IoC.GetInstance<IResourceRepository>(),
                                       IoC.GetInstance<IConsumer<AuditEvent>>(),
                                       IoC.GetInstance<IAuditFactory>())
        {
            Check.Assert(!string.IsNullOrEmpty(ResourceType), "ResourceType instance is null or empty");

            DefaultResourceCulture = "en";
            UserName = "";
        }

        public ResourceFacade(//string resourceType,
                              IResourceRepository resourceRepository, 
                              IConsumer<AuditEvent> handler, 
                              IAuditFactory auditFactory)
        {
            _resourceRepository = resourceRepository;
            _handler = handler;
            _auditFactory = auditFactory;
            DefaultResourceCulture = "en";
        }

        #endregion

        #region implement functions

        public string UserName { get; set; }

        public string ResourceType { get; set; }

        public string DefaultResourceCulture {get;private set;}

        public string GetResourceByCultureAndKeyInternal(CultureInfo culture, string resourceKey)
        {
            Check.Assert(_resourceRepository != null, "Resource Repository is null");
            Check.Assert(!string.IsNullOrEmpty(ResourceType), "ResourceType instance is null or empty");
            Check.Assert(culture != null, "CultureInfo instance is null");
            Check.Assert(!string.IsNullOrEmpty(resourceKey), "ResourceKey is null or empty");

            try
            {
                // we should only get one back, but just in case, we'll iterate reader results
                StringCollection resources = new StringCollection();
                string resourceValue = null;

                var results = _resourceRepository.GetBy(x => string.Compare(x.ResourceType, ResourceType) == 0 &&
                                          string.Compare(x.CultureCode, culture.Name) == 0 &&
                                          string.Compare(x.ResourceKey, resourceKey) == 0
                                          ).ToList();

                results.ForEach(x => { resources.Add(x.ResourceValue); });

                // we should only get 1 back, this is just to verify the tables aren't incorrect
                if (resources.Count == 0)
                {
                    // is this already fallback location?
                    if (culture.Name == this.DefaultResourceCulture)
                    {
                        throw new InvalidOperationException(String.Format(Thread.CurrentThread.CurrentUICulture, "Unable to find a default resource for {0}.", resourceKey));
                    }

                    // try to get parent culture
                    culture = culture.Parent;
                    if (culture.Name.Length == 0)
                    {
                        // there isn't a parent culture, change to neutral
                        culture = new CultureInfo(this.DefaultResourceCulture);
                    }
                    resourceValue = this.GetResourceByCultureAndKeyInternal(culture, resourceKey);
                }
                else if (resources.Count == 1)
                {
                    resourceValue = resources[0];
                }
                else
                {
                    // if > 1 row returned, log an error, we shouldn't have > 1 value for a resourceKey!
                    throw new DataException(String.Format(Thread.CurrentThread.CurrentUICulture, "An internal data error has occurred. A duplicate resource entry was found for {0}.", resourceKey));
                }

                return resourceValue;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public ListDictionary GetResourcesByCulture(CultureInfo culture)
        {
            Check.Assert(_resourceRepository != null, "Resource Repository is null");
            Check.Assert(!string.IsNullOrEmpty(ResourceType), "ResourceType instance is null");
            Check.Assert(culture != null, "CultureInfo instance is null");

            try
            {
                // make sure we have a default culture at least
                if (culture == null || culture.Name.Length == 0)
                {
                    culture = new CultureInfo(this.DefaultResourceCulture);
                }

                var results = _resourceRepository.GetBy(x => string.Compare(x.ResourceType, ResourceType) == 0 &&
                                            string.Compare(x.CultureCode, culture.Name) == 0).ToList();

                // create the dictionary
                ListDictionary resourceDictionary = new ListDictionary();

                results.ForEach(x => { resourceDictionary.Add(x.ResourceKey, x.ResourceValue); });

                // TODO: check dispose results
                return resourceDictionary;
            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public string GetResourceByCultureAndKey(CultureInfo culture, string resourceKey)
        {
            Check.Assert(_resourceRepository != null, "Resource Repository is null");
            Check.Assert(culture != null, "CultureInfo instance is null");
            Check.Assert(!string.IsNullOrEmpty(resourceKey), "ResourceKey is null or empty");

            try
            {
                string resourceValue = string.Empty;

                // make sure we have a default culture at least
                if (culture == null || culture.Name.Length == 0)
                {
                    culture = new CultureInfo(this.DefaultResourceCulture);
                }

                // recurse to find resource, includes fallback behavior
                resourceValue = this.GetResourceByCultureAndKeyInternal(culture, resourceKey);

                return resourceValue;
            }
            catch(Exception ex)
            {
                _logger.Error(ex.Message);
                throw ex;
            }
        }

        #endregion
    }
}